﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using CID.Domain.Audit;
using CID.Domain.Development;
using CID.Domain.Operations;
using CID.Execution.Controller.Exceptions;
using CID.Execution.Support.Contracts;
using CID.Execution.Support.DeploymentInitiation;
using CID.Execution.Support.Exceptions;
using NailsFramework.IoC;
using NailsFramework.Persistence;
using NUnit.Framework;
using Environment = CID.Domain.Operations.Environment;

namespace CID.Execution.Controller.Tests
{
    [TestFixture]
    class ControllerTests : BaseControllerTests
    {
        private const string Location1DeactivationCompleted = "Location 1 Deactivation Completed";
        private const string Location2DeactivationCompleted = "Location 2 Deactivation Completed";
        private const string Location1DeploymentCompleted = "Location 1 Deployment Completed";
        private const string Location2DeploymentCompleted = "Location 2 Deployment Completed";
        private const string Location1ActivationCompleted = "Location 1 Activation Completed";
        private const string Location2ActivationCompleted = "Location 2 Activation Completed";
        private const string Location1DeactivationFailed = "Location 1 Deactivation Failed";
        private const string Location2DeactivationFailed = "Location 2 Deactivation Failed";
        private const string Location1DeploymentFailed = "Location 1 Deployment Failed";
        private const string Location2DeploymentFailed = "Location 2 Deployment Failed";
        private const string Location1ActivationFailed = "Location 1 Activation Failed";
        private const string Location2ActivationFailed = "Location 2 Activation Failed";

        #region AgentServiceStub
        [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
        private class AgentServiceStub : IAgentService
        {
            public const string FailingLocationMessage = "Failed on purpose for testing";
            public static readonly List<int> LocationsToFail = new List<int>();

            public static readonly List<int> DeactivatedLocations = new List<int>();
            public static readonly List<int> DeployedLocations = new List<int>();
            public static readonly List<int> ActivatedLocations = new List<int>();

            public static void ClearLocationsLists()
            {
                LocationsToFail.Clear();
                DeactivatedLocations.Clear();
                ActivatedLocations.Clear();
                DeployedLocations.Clear();
            }

            public void StartDeactivation(Execution.Support.Scripting.LocationDeactivationScript script)
            {
                if (LocationsToFail.Contains(script.LocationId)) throw new Exception(FailingLocationMessage);
                DeactivatedLocations.Add(script.LocationId);
            }

            public void StartDeployment(Execution.Support.Scripting.LocationDeploymentScript script)
            {
                if (LocationsToFail.Contains(script.LocationId)) throw new Exception(FailingLocationMessage);
                DeployedLocations.Add(script.LocationId);
            }

            public void StartActivation(Execution.Support.Scripting.LocationActivationScript script)
            {
                if (LocationsToFail.Contains(script.LocationId)) throw new Exception(FailingLocationMessage);
                ActivatedLocations.Add(script.LocationId);
            }
        }
        #endregion

        [Inject]
        public IDeploymentInitiator DeploymentInitiator { private get; set; }
        [Inject]
        public IControllerService Controller { private get; set; }
        [Inject]
        public IBag<ReleaseDeploymentLog> DeploymentLogs { private get; set; }
        [Inject]
        public IBag<Environment> Environments { private get; set; }
        [Inject]
        public IBag<Release> Releases { private get; set; }

        private ServiceHost serviceStubHost;

        public override void SetUp()
        {
            base.SetUp();

            AgentServiceStub.ClearLocationsLists();
            serviceStubHost = new ServiceHost(typeof(AgentServiceStub), new Uri(String.Format("http://{0}/DeploymentAgent", InfrastructureData.DeploymentAgent.Address)));
            serviceStubHost.Open();
        }

        public override void TearDown()
        {
            serviceStubHost.Close();
            base.TearDown();
        }

        #region InitiateDeployment (By IDs)
        [Test]
        public void TestInitiateDeploymentByIds_WithValidRelease_ShouldStartDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentState);
            Assert.AreEqual(2, deploymentState.PendingDeactivations);
            Assert.AreEqual(2, deploymentState.PendingDeployments);
            Assert.AreEqual(2, deploymentState.PendingActivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentLog);
            Assert.AreEqual(DevelopmentData.ActiveRelease.Id, deploymentLog.Release.Id);
            Assert.AreEqual(OperationsData.Environment.Id, deploymentLog.Environment.Id);
            Assert.AreEqual(UsersData.Operator.Id, deploymentLog.Responsible.Id);
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.IsEmpty(AgentServiceStub.ActivatedLocations);
            CollectionAssert.IsEmpty(AgentServiceStub.DeployedLocations);
        }

        [Test, ExpectedException(typeof(MissingVersionDeploymentPackageException))]
        public void TestInitiateDeploymentByIds_WithInvalidRelease_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.InvalidRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByIds_WithNonExistentRelease_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(-1, OperationsData.Environment.Id, UsersData.Operator.Id);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByIds_WithNonExistentEnvironment_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, -1, UsersData.Operator.Id);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByIds_WithNonExistentUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, -1);
        }
        #endregion

        #region InitiateDeployment (By Names)
        [Test]
        public void TestInitiateDeploymentByNames_WithValidRelease_ShouldStartDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Name, OperationsData.Environment.Name, UsersData.Operator.UserName);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentState);
            Assert.AreEqual(2, deploymentState.PendingDeactivations);
            Assert.AreEqual(2, deploymentState.PendingDeployments);
            Assert.AreEqual(2, deploymentState.PendingActivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentLog);
            Assert.AreEqual(DevelopmentData.ActiveRelease.Id, deploymentLog.Release.Id);
            Assert.AreEqual(OperationsData.Environment.Id, deploymentLog.Environment.Id);
            Assert.AreEqual(UsersData.Operator.Id, deploymentLog.Responsible.Id);
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.IsEmpty(AgentServiceStub.ActivatedLocations);
            CollectionAssert.IsEmpty(AgentServiceStub.DeployedLocations);
        }

        [Test, ExpectedException(typeof(MissingVersionDeploymentPackageException))]
        public void TestInitiateDeploymentByNames_WithInvalidRelease_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.InvalidRelease.Name, OperationsData.Environment.Name, UsersData.Operator.UserName);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByNames_WithNonExistentRelease_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(String.Empty, OperationsData.Environment.Name, UsersData.Operator.UserName);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByNames_WithNonExistentEnvironment_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Name, String.Empty, UsersData.Operator.UserName);
        }

        [Test, ExpectedException(typeof(InvalidDeploymentInitiationException))]
        public void TestInitiateDeploymentByNames_WithNonExistentUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Name, OperationsData.Environment.Name, String.Empty);
        }
        #endregion

        #region InitiateDeployment Authorization
        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_ComponentManagerUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.ComponentManager.Id);
        }

        [Test]
        public void InitiateDeployment_OperatorUser_DeploymentShouldBeInitiated()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentState);
        }

        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_ReleaseManagerUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.ReleaseManager.Id);
        }

        [Test]
        public void InitiateDeployment_ManagerUser_DeploymentShouldBeInitiated()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Manager.Id);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            Assert.IsNotNull(deploymentState);
        }

        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_EnvironmentManagerUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.EnvironmentManager.Id);
        }

        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_DeveloperUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Developer.Id);
        }

        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_RolelessUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Roleless.Id);

        }

        [Test, ExpectedException(typeof(UserActionUnauthorizedException))]
        public void InitiateDeployment_ArchitectUser_ShouldThrowException()
        {
            DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Architect.Id);

        }

        #endregion


        #region NotifyDeactivationCompleted
        [Test]
        public void TestNotifyDeactivationCompletedOnce_ShouldReducePendingActivations()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var originalPendingDeactivations = deploymentState.PendingDeactivations;

            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);

            Assert.AreEqual(originalPendingDeactivations - 1, deploymentState.PendingDeactivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.IsEmpty(AgentServiceStub.ActivatedLocations);
            CollectionAssert.IsEmpty(AgentServiceStub.DeployedLocations);
        }

        [Test]
        public void TestNotifyDeactivationCompletedTwice_ShouldReducePendingActivationsAndStartDeployments()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);

            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            Assert.AreEqual(0, deploymentState.PendingDeactivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location2.Id);
            CollectionAssert.IsEmpty(AgentServiceStub.ActivatedLocations);
        }
        #endregion

        #region NotifyDeploymentCompleted
        [Test]
        public void TestNotifyDeploymentCompletedOnce_ShouldReducePendingDeployments()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var originalPendingDeployments = deploymentState.PendingDeployments;

            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);

            Assert.AreEqual(originalPendingDeployments - 1, deploymentState.PendingDeployments);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1DeploymentCompleted, deploymentLog.Details);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location2.Id);
            CollectionAssert.IsEmpty(AgentServiceStub.ActivatedLocations);
        }

        [Test]
        public void TestNotifyDeploymentCompletedTwice_ShouldReducePendingDeploymentsAndStartActivations()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var originalPendingDeployments = deploymentState.PendingDeployments;

            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);

            Assert.AreEqual(0, deploymentState.PendingDeployments);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeploymentCompleted, deploymentLog.Details);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location2.Id);
        }
        #endregion

        #region NotifyActivationCompleted
        [Test]
        public void TestNotifyActivationCompletedOnce_ShouldReducePendingActivations()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var originalPendingActivations = deploymentState.PendingActivations;

            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);

            Assert.AreEqual(originalPendingActivations - 1, deploymentState.PendingActivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1ActivationCompleted, deploymentLog.Details);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location2.Id);
        }

        [Test]
        public void TestNotifyActivationCompletedTwice_ShouldReducePendingActivationsAndFinishDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);

            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            Assert.AreEqual(0, deploymentState.PendingActivations);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            StringAssert.Contains(Location1ActivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2ActivationCompleted, deploymentLog.Details);
            Assert.AreEqual(DeploymentStatus.Succeeded, deploymentLog.Status);
            Assert.IsNotNull(deploymentLog.EndTimestamp);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeactivatedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.DeployedLocations, OperationsData.Location2.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location1.Id);
            CollectionAssert.Contains(AgentServiceStub.ActivatedLocations, OperationsData.Location2.Id);
        }
        #endregion

        #region General
        [Test]
        public void TestDeploymentWithErrorInDeactivation_ShouldFinishDeploymentWithError()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, true);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            Assert.IsNotNull(deploymentLog.EndTimestamp);
        }

        [Test]
        public void TestDeploymentWithErrorInDeployment_ShouldFinishDeploymentWithError()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, true);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            Assert.IsNotNull(deploymentLog.EndTimestamp);
        }

        [Test]
        public void TestDeploymentWithErrorInActivation_ShouldFinishDeploymentWithError()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, true);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            Assert.IsNotNull(deploymentLog.EndTimestamp);
        }
        #endregion

        #region Error Flows
        private static string GetStartStageErrorMessage(string stage, ILocation location)
        {
            return String.Format("The {0} stage failed to start in location {1}", stage, location.Name);
        }

        private static string GetEndStageErrorMessage(string stage, ILocation location)
        {
            return String.Format("The {0} stage ended with errors in location {1}\r\nThe {0} failed due to a previous error", stage, location.Name);
        }

        private static string GetStageSkippedMessage(string stage, ILocation location)
        {
            return String.Format("The {0} stage was skipped in location {1} due to previous errors", stage, location.Name);
        }

        #region (OnStartingDeactivationStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnStartingDeactivation_ShouldWaitForDeactivationEndOnOtherLocations()
        {
            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);

            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            
            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(1, deploymentState.PendingDeactivations);
            Assert.AreEqual(2, deploymentState.PendingDeployments);
            Assert.AreEqual(2, deploymentState.PendingActivations);
            Assert.AreEqual(1, deploymentState.DeactivationErrors);
            Assert.AreEqual(0, deploymentState.DeploymentErrors);
            Assert.AreEqual(0, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deactivation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deactivation", OperationsData.Location1), deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnStartingDeactivation_ShouldFailDeployment()
        {
            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);
            AgentServiceStub.LocationsToFail.Add(OperationsData.Location2.Id);

            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deactivation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deactivation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deactivation", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deactivation", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Deployment", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location2), deploymentLog.Details);
        }
        #endregion

        #region (OnStartingDeploymentStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnStartingDeployment_ShouldWaitForDeploymentEndOnOtherLocations()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(0, deploymentState.PendingDeactivations);
            Assert.AreEqual(1, deploymentState.PendingDeployments);
            Assert.AreEqual(2, deploymentState.PendingActivations);
            Assert.AreEqual(0, deploymentState.DeactivationErrors);
            Assert.AreEqual(1, deploymentState.DeploymentErrors);
            Assert.AreEqual(0, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnStartingDeployment_ShouldFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);
            AgentServiceStub.LocationsToFail.Add(OperationsData.Location2.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Deployment", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Deployment", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location2), deploymentLog.Details);
        }
        #endregion

        #region (OnStartingActivationStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnStartingActivation_ShouldWaitForActivationEndOnOtherLocations()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(0, deploymentState.PendingDeactivations);
            Assert.AreEqual(0, deploymentState.PendingDeployments);
            Assert.AreEqual(1, deploymentState.PendingActivations);
            Assert.AreEqual(0, deploymentState.DeactivationErrors);
            Assert.AreEqual(0, deploymentState.DeploymentErrors);
            Assert.AreEqual(1, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Activation", OperationsData.Location1), deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnStartingActivation_ShouldFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            AgentServiceStub.LocationsToFail.Add(OperationsData.Location1.Id);
            AgentServiceStub.LocationsToFail.Add(OperationsData.Location2.Id);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(AgentServiceStub.FailingLocationMessage, deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStartStageErrorMessage("Activation", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetEndStageErrorMessage("Activation", OperationsData.Location2), deploymentLog.Details);
        }
        #endregion

        #region (OnExecutingDeactivationStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnExecutingDeactivation_ShoulSkipDeploymentForFailedLocationAndWaitDeploymentForTheRest()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationFailed, true);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(0, deploymentState.PendingDeactivations);
            Assert.AreEqual(1, deploymentState.PendingDeployments);
            Assert.AreEqual(2, deploymentState.PendingActivations);
            Assert.AreEqual(1, deploymentState.DeactivationErrors);
            Assert.AreEqual(0, deploymentState.DeploymentErrors);
            Assert.AreEqual(0, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationFailed, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnExecutingDeactivation_ShouldSkipDeploymentAndActivationAndThenFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);

            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationFailed, true);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationFailed, true);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationFailed, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationFailed, deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Deployment", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Deployment", OperationsData.Location2), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location2), deploymentLog.Details);
        }
        #endregion

        #region (OnExecutingDeploymentStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnExecutingDeployment_ShoulSkipActivationForFailedLocationAndWaitActivationForTheRest()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentFailed, true);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.AreEqual(0, deploymentState.PendingDeactivations);
            Assert.AreEqual(0, deploymentState.PendingDeployments);
            Assert.AreEqual(1, deploymentState.PendingActivations);
            Assert.AreEqual(0, deploymentState.DeactivationErrors);
            Assert.AreEqual(1, deploymentState.DeploymentErrors);
            Assert.AreEqual(0, deploymentState.ActivationErrors);
            Assert.IsTrue(deploymentState.LocationHasErrors(OperationsData.Location1.Id));
            Assert.IsFalse(deploymentState.LocationHasErrors(OperationsData.Location2.Id));
            Assert.AreEqual(DeploymentStatus.Started, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1DeploymentFailed, deploymentLog.Details);
            StringAssert.Contains(Location2DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location1), deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnExecutingDeployment_ShouldSkipActivationAndThenFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);

            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentFailed, true);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentFailed, true);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1DeploymentFailed, deploymentLog.Details);
            StringAssert.Contains(Location2DeploymentFailed, deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location1), deploymentLog.Details);
            StringAssert.Contains(GetStageSkippedMessage("Activation", OperationsData.Location2), deploymentLog.Details);
        }
        #endregion

        #region (OnStartingActivationStage)
        [Test]
        public void TestDeploy_OneFailingLocationOnExecutingActivation_ShouldFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);

            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationFailed, true);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1ActivationFailed, deploymentLog.Details);
            StringAssert.Contains(Location2ActivationCompleted, deploymentLog.Details);
        }

        [Test]
        public void TestDeploy_AllFailingLocationOnExecutingActivation_ShouldFailDeployment()
        {
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);

            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationFailed, true);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationFailed, true);

            var deploymentState = DeploymentStateRepository.Instance.Get(deploymentUniqueIdentifier);
            var deploymentLog = DeploymentLogs.SingleOrDefault(x => x.DeploymentUniqueIdentifier == deploymentUniqueIdentifier);
            Assert.IsNull(deploymentState);
            Assert.AreEqual(DeploymentStatus.Failed, deploymentLog.Status);
            StringAssert.Contains(Location1DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeactivationCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(Location2DeploymentCompleted, deploymentLog.Details);
            StringAssert.Contains(Location1ActivationFailed, deploymentLog.Details);
            StringAssert.Contains(Location2ActivationFailed, deploymentLog.Details);
        }
        #endregion

        #endregion

        #region Deployment ending

        private Environment GetEnvironment()
        {
            return Environments.GetById(OperationsData.Environment.Id);
        }

        private Release GetActiveRelease()
        {
            return Releases.GetById(DevelopmentData.ActiveRelease.Id);
        }

        [Test]
        public void TestEndDeployment_SuccessfullResultNonProductionEnvironment_ShouldUpdateEnvironmentStateAndKeepReleaseActive()
        {
            var originalEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var currentEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            CollectionAssert.AreNotEquivalent(originalEnvironmentState, currentEnvironmentState);
            CollectionAssert.IsSubsetOf(DevelopmentData.ActiveRelease.Versions.Select(x => x.Id).ToArray(), currentEnvironmentState);
            Assert.AreEqual(ReleaseStatus.Active, GetActiveRelease().Status);
        }

        [Test]
        public void TestEndDeployment_SuccessfullResultProductionEnvironment_ShouldUpdateEnvironmentStateAndSetReleaseReleased()
        {
            GetEnvironment().IsProduction = true;
            var originalEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationCompleted, false);

            var currentEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            CollectionAssert.AreNotEquivalent(originalEnvironmentState, currentEnvironmentState);
            CollectionAssert.IsSubsetOf(DevelopmentData.ActiveRelease.Versions.Select(x => x.Id).ToArray(), currentEnvironmentState);
            Assert.AreEqual(ReleaseStatus.Released, GetActiveRelease().Status);
        }

        [Test]
        public void TestEndDeployment_NotSuccessfullResultNonProductionEnvironment_ShouldNotUpdateEnvironmentStateAndKeepReleaseActive()
        {
            var originalEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationFailed, true);

            var currentEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            CollectionAssert.AreEquivalent(originalEnvironmentState, currentEnvironmentState);
            CollectionAssert.IsNotSubsetOf(DevelopmentData.ActiveRelease.Versions.Select(x => x.Id).ToArray(), currentEnvironmentState);
            Assert.AreEqual(ReleaseStatus.Active, GetActiveRelease().Status);
        }

        [Test]
        public void TestEndDeployment_NotSuccessfullResultProductionEnvironment_ShouldNotUpdateEnvironmentStateAndKeepReleaseActive()
        {
            GetEnvironment().IsProduction = true;
            var originalEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            var deploymentUniqueIdentifier = DeploymentInitiator.InitiateDeployment(DevelopmentData.ActiveRelease.Id, OperationsData.Environment.Id, UsersData.Operator.Id);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeactivationCompleted, false);
            Controller.NotifyDeactivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeactivationCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1DeploymentCompleted, false);
            Controller.NotifyDeploymentCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2DeploymentCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location1.Id, Location1ActivationCompleted, false);
            Controller.NotifyActivationCompleted(deploymentUniqueIdentifier, OperationsData.Location2.Id, Location2ActivationFailed, true);

            var currentEnvironmentState = GetEnvironment().ActiveVersions.Select(x => x.Version.Id).ToArray();
            CollectionAssert.AreEquivalent(originalEnvironmentState, currentEnvironmentState);
            CollectionAssert.IsNotSubsetOf(DevelopmentData.ActiveRelease.Versions.Select(x => x.Id).ToArray(), currentEnvironmentState);
            Assert.AreEqual(ReleaseStatus.Active, GetActiveRelease().Status);
        }
        #endregion
    }
}
